﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EventTrigger.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Enhanced event trigger
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Windows.Interactivity
{
    using System;
    using System.Reactive.Linq;
    using System.Reactive.Subjects;
    using System.Windows;

    ///<summary>
    /// Enhanced event trigger
    ///</summary>
    public class EventTrigger : System.Windows.Interactivity.EventTrigger
    {
        /// <summary>
        /// Identifies the <see cref="Throttle"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ThrottleProperty = DependencyProperty.Register("Throttle", typeof(TimeSpan),
            typeof(EventTrigger), new PropertyMetadata(
                default(TimeSpan), ThrottlePropetyChanged));

        /// <summary>
        /// Identifies the <see cref="Context"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ContextProperty = DependencyProperty.Register("Context", typeof(object),
            typeof(object), new PropertyMetadata(
                null, ContextPropertyChanged));

        private bool _isPrivateWrite;
        private Subject<EventArgs> _subject;

        /// <summary>
        /// Gets or sets the throttle.
        /// </summary>
        /// <value>The throttle.</value>
        public TimeSpan Throttle
        {
            get { return (TimeSpan)GetValue(ThrottleProperty); }
            set { SetValue(ThrottleProperty, value); }
        }

        /// <summary>
        /// Gets the triggers current context.
        /// </summary>
        /// <value>The triggers current context.</value>
        /// <remarks>
        /// In the case of an event trigger the context is the events event args.
        /// </remarks>
        public object Context
        {
            get { return GetValue(ContextProperty); }
            private set
            {
                _isPrivateWrite = true;
                SetValue(ContextProperty, value);
                _isPrivateWrite = false;
            }
        }

        protected Subject<EventArgs> Subject
        {
            get
            {
                return this._subject;
            }
            set
            {
                if (_subject != value)
                {
                    if (_subject != null)
                    {
                        _subject.Dispose();
                    }

                    _subject = value;
                }
            }
        }

        private static void ContextPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            // context is readonly
            var trigger = ((EventTrigger)obj);
            if (!trigger._isPrivateWrite)
            {
                trigger.Context = e.OldValue;
                throw new InvalidOperationException("Property is readonly.");
            }
        }

        private static void ThrottlePropetyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            ((EventTrigger)obj).ThrottleChanged();
        }

        private void ThrottleChanged()
        {
            Subject = null;
            if (Throttle != default(TimeSpan))
            {
                Subject = new Subject<EventArgs>();
                Subject.Throttle(Throttle).ObserveOnDispatcher().Subscribe(OnInvokeEvent);
            }
        }

        protected override void OnSourceChanged(object oldSource, object newSource)
        {
            base.OnSourceChanged(oldSource, newSource);
            this.ThrottleChanged();
        }

        protected override sealed void OnEvent(EventArgs eventArgs)
        {
            if (this.Subject != null)
            {
                this.Subject.OnNext(eventArgs);
            }
            else
            {
                this.OnInvokeEvent(eventArgs);
            }
        }

        protected virtual void OnInvokeEvent(EventArgs eventArgs)
        {
            this.Context = eventArgs;
            base.OnEvent(eventArgs);
            this.Context = null;
        }
    }
}